//! Transaction support for Redis-rs2
//! 
//! This module provides basic transaction support for batch operations.
//! It implements a simple transaction mechanism that allows multiple
//! operations to be executed atomically.

// use crate::error::Result;  // 已注释：未使用的导入
use std::collections::HashMap;
use serde::{Serialize, Deserialize};

/// Represents a single operation in a transaction
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TransactionOperation {
    Set { key: String, value: String },
    Del { key: String },
    Incr { key: String, amount: i64 },
    HSet { key: String, field: String, value: String },
    HDel { key: String, field: String },
    LPush { key: String, values: Vec<String> },
    RPush { key: String, values: Vec<String> },
    LPop { key: String },
    RPop { key: String },
    SAdd { key: String, members: Vec<String> },
    SRem { key: String, members: Vec<String> },
    ZAdd { key: String, members: Vec<(f64, String)> },
    ZRem { key: String, members: Vec<String> },
}

/// Result of a single transaction operation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TransactionResult {
    Ok,
    IntegerReply(i64),
    StringReply(String),
    ArrayReply(Vec<String>),
    FloatReply(f64),
    BooleanReply(bool),
    Error(String),
}

/// A transaction that can batch multiple operations
#[derive(Debug, Default)]
pub struct Transaction {
    /// List of operations to execute
    operations: Vec<TransactionOperation>,
    /// Whether the transaction is currently being watched
    watching: bool,
    /// Keys being watched for changes
    watched_keys: std::collections::HashSet<String>,
}

impl Transaction {
    /// Create a new empty transaction
    pub fn new() -> Self {
        Self {
            operations: Vec::new(),
            watching: false,
            watched_keys: std::collections::HashSet::new(),
        }
    }
    
    /// Add an operation to the transaction
    pub fn add_operation(&mut self, operation: TransactionOperation) {
        self.operations.push(operation);
    }
    
    /// Start watching keys for changes
    pub fn watch(&mut self, keys: Vec<String>) {
        self.watching = true;
        self.watched_keys.extend(keys);
    }
    
    /// Stop watching keys
    pub fn unwatch(&mut self) {
        self.watching = false;
        self.watched_keys.clear();
    }
    
    /// Check if a key is being watched
    pub fn is_watching(&self, key: &str) -> bool {
        self.watching && self.watched_keys.contains(key)
    }
    
    /// Get the number of operations in the transaction
    pub fn operation_count(&self) -> usize {
        self.operations.len()
    }
    
    /// Check if the transaction is empty
    pub fn is_empty(&self) -> bool {
        self.operations.is_empty()
    }
    
    /// Clear all operations
    pub fn clear(&mut self) {
        self.operations.clear();
    }
    
    /// Get all operations (for execution)
    pub fn operations(&self) -> &[TransactionOperation] {
        &self.operations
    }
    
    /// Discard the transaction
    pub fn discard(&mut self) {
        self.operations.clear();
        self.unwatch();
    }
}

/// Batch operation support for multiple keys
#[derive(Debug)]
pub struct BatchOperation {
    /// Map of operations by key
    operations: HashMap<String, Vec<TransactionOperation>>,
}

impl BatchOperation {
    /// Create a new batch operation
    pub fn new() -> Self {
        Self {
            operations: HashMap::new(),
        }
    }
    
    /// Add an operation for a specific key
    pub fn add_key_operation(&mut self, key: String, operation: TransactionOperation) {
        self.operations.entry(key).or_default().push(operation);
    }
    
    /// Get all operations grouped by key
    pub fn operations(&self) -> &HashMap<String, Vec<TransactionOperation>> {
        &self.operations
    }
    
    /// Get operation count
    pub fn operation_count(&self) -> usize {
        self.operations.values().map(|ops| ops.len()).sum()
    }
    
    /// Check if batch is empty
    pub fn is_empty(&self) -> bool {
        self.operations.is_empty()
    }
    
    /// Clear all operations
    pub fn clear(&mut self) {
        self.operations.clear();
    }
}

impl Default for BatchOperation {
    fn default() -> Self {
        Self::new()
    }
}

/// Utility functions for creating transaction operations
impl TransactionOperation {
    /// Create a SET operation
    pub fn set(key: String, value: String) -> Self {
        Self::Set { key, value }
    }
    
    /// Create a DEL operation
    pub fn del(key: String) -> Self {
        Self::Del { key }
    }
    
    /// Create an INCR operation
    pub fn incr(key: String, amount: i64) -> Self {
        Self::Incr { key, amount }
    }
    
    /// Create an HSET operation
    pub fn hset(key: String, field: String, value: String) -> Self {
        Self::HSet { key, field, value }
    }
    
    /// Create an HDEL operation
    pub fn hdel(key: String, field: String) -> Self {
        Self::HDel { key, field }
    }
    
    /// Create an LPUSH operation
    pub fn lpush(key: String, values: Vec<String>) -> Self {
        Self::LPush { key, values }
    }
    
    /// Create an RPUSH operation
    pub fn rpush(key: String, values: Vec<String>) -> Self {
        Self::RPush { key, values }
    }
    
    /// Create an LPOP operation
    pub fn lpop(key: String) -> Self {
        Self::LPop { key }
    }
    
    /// Create an RPOP operation
    pub fn rpop(key: String) -> Self {
        Self::RPop { key }
    }
    
    /// Create an SADD operation
    pub fn sadd(key: String, members: Vec<String>) -> Self {
        Self::SAdd { key, members }
    }
    
    /// Create an SREM operation
    pub fn srem(key: String, members: Vec<String>) -> Self {
        Self::SRem { key, members }
    }
    
    /// Create a ZADD operation
    pub fn zadd(key: String, members: Vec<(f64, String)>) -> Self {
        Self::ZAdd { key, members }
    }
    
    /// Create a ZREM operation
    pub fn zrem(key: String, members: Vec<String>) -> Self {
        Self::ZRem { key, members }
    }
    
    /// Get the key affected by this operation
    pub fn affected_key(&self) -> &str {
        match self {
            Self::Set { key, .. } => key,
            Self::Del { key } => key,
            Self::Incr { key, .. } => key,
            Self::HSet { key, .. } => key,
            Self::HDel { key, .. } => key,
            Self::LPush { key, .. } => key,
            Self::RPush { key, .. } => key,
            Self::LPop { key } => key,
            Self::RPop { key } => key,
            Self::SAdd { key, .. } => key,
            Self::SRem { key, .. } => key,
            Self::ZAdd { key, .. } => key,
            Self::ZRem { key, .. } => key,
        }
    }
    
    /// Check if this operation modifies data
    pub fn is_write_operation(&self) -> bool {
        matches!(self, 
            Self::Set { .. } | 
            Self::Del { .. } | 
            Self::Incr { .. } |
            Self::HSet { .. } |
            Self::HDel { .. } |
            Self::LPush { .. } |
            Self::RPush { .. } |
            Self::LPop { .. } |
            Self::RPop { .. } |
            Self::SAdd { .. } |
            Self::SRem { .. } |
            Self::ZAdd { .. } |
            Self::ZRem { .. }
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_transaction_basic() {
        let mut txn = Transaction::new();
        
        assert!(txn.is_empty());
        assert_eq!(txn.operation_count(), 0);
        
        txn.add_operation(TransactionOperation::set("key1".to_string(), "value1".to_string()));
        txn.add_operation(TransactionOperation::incr("counter".to_string(), 1));
        
        assert!(!txn.is_empty());
        assert_eq!(txn.operation_count(), 2);
        
        txn.clear();
        assert!(txn.is_empty());
    }
    
    #[test]
    fn test_transaction_watch() {
        let mut txn = Transaction::new();
        
        assert!(!txn.is_watching("key1"));
        
        txn.watch(vec!["key1".to_string(), "key2".to_string()]);
        
        assert!(txn.is_watching("key1"));
        assert!(txn.is_watching("key2"));
        assert!(!txn.is_watching("key3"));
        
        txn.unwatch();
        assert!(!txn.is_watching("key1"));
    }
    
    #[test]
    fn test_batch_operation() {
        let mut batch = BatchOperation::new();
        
        assert!(batch.is_empty());
        assert_eq!(batch.operation_count(), 0);
        
        batch.add_key_operation(
            "key1".to_string(), 
            TransactionOperation::set("key1".to_string(), "value1".to_string())
        );
        batch.add_key_operation(
            "key1".to_string(), 
            TransactionOperation::incr("key1".to_string(), 1)
        );
        batch.add_key_operation(
            "key2".to_string(), 
            TransactionOperation::del("key2".to_string())
        );
        
        assert!(!batch.is_empty());
        assert_eq!(batch.operation_count(), 3);
        
        let operations = batch.operations();
        assert_eq!(operations.len(), 2); // Two keys
        assert_eq!(operations.get("key1").unwrap().len(), 2);
        assert_eq!(operations.get("key2").unwrap().len(), 1);
    }
    
    #[test]
    fn test_transaction_operation_utilities() {
        let op = TransactionOperation::set("test_key".to_string(), "test_value".to_string());
        assert_eq!(op.affected_key(), "test_key");
        assert!(op.is_write_operation());
        
        let op = TransactionOperation::del("test_key".to_string());
        assert_eq!(op.affected_key(), "test_key");
        assert!(op.is_write_operation());
        
        let op = TransactionOperation::incr("counter".to_string(), 5);
        assert_eq!(op.affected_key(), "counter");
        assert!(op.is_write_operation());
    }
}